#ifndef DSPSIMULATOR_SPLOOPBUFFER_H
#define DSPSIMULATOR_SPLOOPBUFFER_H

#include <memory>
#include <utility>
#include <vector>
#include <bitset>
#include <simulator/type/type.h>
#include <dspSimulator/targetMachine//dspInstruction.h>

class DspMachine;

class Instruction;

enum class SPLoopKind {
    SPLoop,
    SPLoopD,
    SPLoopW,
    UnKnown
};

enum class SPLoopBufferState {
    IDLE,
    ACTIVE
};

class SPLoopBuffer {
private:
    SPLoopKind m_spLoopKind{SPLoopKind::UnKnown};     // 启动sploop的指令类型
    SPLoopBufferState m_spLoopBufferState{SPLoopBufferState::IDLE}; // sploopBuffer的状态
    std::bitset<4> m_stageKind;      // reload | drain | fetch | load
    u32 m_currentDrainIndex{0xffffffff};
    u32 m_ii{0};
    u32 m_LBC[2] = {0};
    std::bitset<4> m_condition{0};
    std::shared_ptr<DspMachine> m_dspMachine;
    std::vector<std::shared_ptr<DspInstruction>> m_buffer;          // 缓存在sploop buffer中的dsp指令数据
    std::vector<u32> m_stageNum;                              // 缓存中每条指令对应的阶段数
    std::vector<u32> m_offset;                                // 缓存中每条指令在当前stage的偏移量
    std::vector<bool> m_instState;                            // 缓存中每条指令的状态，0表示已屏蔽，1表示可加载
    u32 m_fstg{0};
    u32 m_fcyc{0};
    bool m_isTerminate{false};
    u32 m_numOfStage{0};                                      // 当前sploop所处的阶段数
    u32 m_numOfLoad{0};
    u32 m_numOfFetch{0};
    u32 m_numOfDrain{0};
    u32 m_numOfReload{0};
private:

    /**
     * 加载一条指令数据到buffer中
     * @param instruction 需要加载的dsp指令数据
     * @param offset 指令在当前stage的偏移
     */
    void loadInstruction(std::shared_ptr<DspInstruction> instruction, u32 offset,bool state = true);

    /**
     * 检查SPLoop Buffer条件是否满足
     * @return
     */
    bool checkCondition();

    /**
   * 启动SPLoopBuffer，指令为SPLoop
   * @param ii 阶段间隔
   * @param condition sploop系列指令的条件
   */
    void initialWithSPLoop(u32 ii, std::bitset<4> condition = 0);

    /**
     * 启动SPLoopBuffer，指令为SPLoopW
     * @param ii 阶段间隔
     * @param condition sploop系列指令的条件
     */
    void initialWithSPLoopW(u32 ii, std::bitset<4> condition = 0);


    /**
    * 启动SPLoopBuffer，指令为SPLoopD
    * @param ii 阶段间隔
    * @param condition sploop系列指令的条件
    */
    void initialWithSPLoopD(u32 ii, std::bitset<4> condition = 0);

    /**
     * 激活SPLoopBuffer
     */
    void enableSPLoopBuffer();

public:

    /**
     * SPLoopBuffer的构造器
     * @param dspMachine dsp处理器对象
     */
    explicit SPLoopBuffer(std::shared_ptr<DspMachine> dspMachine) :
            m_dspMachine(std::move(dspMachine)) {};

    ~SPLoopBuffer() = default;

    /**
     * 获取启动SPLoop的指令类型
     * @return
     */
    SPLoopKind getSPLoopKind();


    /**
     * 使用sploop指令初始化sploopbuffer
     * @param sploopInstruction
     */
    void initial(std::shared_ptr<DspInstruction> sploopInstruction);

    bool haveFinishDrain();

    /**
     * load阶段，将一个执行包中的所有指令加载到buffer中
     * @param executePacket 执行包
     */
    void loadExecutePacketIntoBuffer(const std::vector<std::shared_ptr<Instruction>> &executePacket);

    /**
     * 检查SPLoopBuffer是否激活
     * @return
     */
    bool isActive();

    /**
     * SPLoopBuffer激活后，每个周期末，LBC增加1，当LBC加1前等于ii时，意味着当前已到达当前stage的边界
     */
    void incrementLBC();

    /**
     * 设置Kernel指令的fstg与fcyc
     * @param fstg
     * @param fcyc
     */
    void setFstgAndFcyc(u32 fstg, u32 fcyc);

    /**
     * 从buffer中取出需要在当前周期执行的指令
     * @return 当前周期执行的指令
     */
    std::vector<std::shared_ptr<DspInstruction>> fetchInstructions();

    /**
     * drain一个阶段的指令，即将一个阶段的指令标识为被屏蔽的
     */
    void drainOneStage();

    /**
     * 通过fstg和fcyc获取epilog阶段的延时
     * @return
     */
    u32 getDelay() const;

    /**
     * 判断当前周期是否到达了某一个stage的边界
     * @return
     */
    bool isReachBoundary() const;

    /**
     * 判断当前SPLoop Buffer终止条件是否满足
     * @return
     */
    void checkTerminate();

    /**
     * 判断当前阶段是否处于Prolog阶段
     * @return
     */
    bool isLoad() {
        return isActive() && m_stageKind[0];
    }

    /**
     * 判断当前阶段是否处于Kernel阶段
     * @return
     */
    bool isFetch() {
        return isActive() && m_stageKind[1];
    }

    /**
     * 判断当前阶段是否处于Epilog阶段
     * @return
     */
    bool isDrain() {
        return isActive() && m_stageKind[2];
    }

    bool isReload() {
        return isActive() && m_stageKind[3];
    }

    bool isTerminate() {
        return isActive() && m_isTerminate;
    };

    /**
     * 设置SPLoop Buffer的阶段的状态
     * @param flag
     */
    void setLoad(bool flag);

    void setFetch(bool flag);

    void setDrain(bool flag);

    void setReload(bool flag);

    /**
     * 关闭SPLoop Buffer，并清空遗留数据
     */
    void disableSPLoopBuffer();

    /**
     * 获取当前已经完成的Prolog、Kernel、Epilog阶段数
     * @return
     */
    u32 getNumOfLoad() const;

    u32 getNumOfFetch() const;

    u32 getNumOfDrain() const;

    u32 getNumOfReload() const;

    /**
     * 判断当前指令是否被屏蔽
     * @param maskBitset  spkernel屏蔽的bitmap
     * @param funcUnit 指令的函数单元
     * @return
     */
    static bool isMaskInst(const std::bitset<8> &maskBitset, FuncUnit funcUnit);
};


#endif //DSPSIMULATOR_SPLOOPBUFFER_H
